/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		acleditor lookup.cpp
 * PURPOSE:		SID and GUID lookup interface and async lookup state
 *				included as part of acleditor.cpp
 */

class AclEditorLookup : public AclEditorRows
{
	auto_refct<Lookup::Translator<SID> > SidTranslator;
	auto_refct<Lookup::Translator<GUID> > GuidTypeTranslator;
	auto_refct<Lookup::Translator<GUID> > GuidInhTypeTranslator;

protected:
	AclEditorLookup(AclEditorInitParams const &Init) :
		SidTranslator(Init.Translators->SidTranslator),
		GuidTypeTranslator(Init.Translators->GuidTypeTranslator),
		GuidInhTypeTranslator(Init.Translators->GuidInhTypeTranslator),
		AclEditorRows(Init)
	{}

private:
	struct LOOKUP_EXCEPTION_CONTEXT
	{
		HWND ListBox;
		USHORT RowIndex;
	};

	static void LookupExceptionProc(PCWSTR Title, PCWSTR Text, PVOID Context)
	{
		LOOKUP_EXCEPTION_CONTEXT *LEContext = reinterpret_cast<LOOKUP_EXCEPTION_CONTEXT *>(Context);

		if(LEContext->RowIndex != MAXUSHORT)
			SetListBoxItemText(LEContext->ListBox, LEContext->RowIndex, Text);
	}

	struct AclEditLookupCompletionBase
	{
		ULONG UpdateIndex, *SrcUpdateIndex;
		auto_refct<ListboxAceRow> Row;
		UCHAR Col;
		auto_refct<AclEditorLookup> Wo;
		StoredException Se;
		BOOLEAN NoErrorDisplay; //don't clobber data with an error if TRUE

		AclEditLookupCompletionBase(ListboxAceRow *Row, UCHAR Col, AclEditorLookup *Wo, BOOLEAN NoErrorDisplay = FALSE) :
			Row(Row),
			Col(Col),
			Wo(Wo),
			NoErrorDisplay(NoErrorDisplay)
		{
			switch(Col)
			{
			case COL_TSTE_SID:
			case COL_TSTE_NAME:
				SrcUpdateIndex = &Row->SidUpdateIndex;
				break;

			case COL_OBJ_TYPE:
				SrcUpdateIndex = &Row->TypeUpdateIndex;
				break;

			case COL_OBJ_INH_TYPE:
				SrcUpdateIndex = &Row->InhTypeUpdateIndex;
				break;
			}

			UpdateIndex = *SrcUpdateIndex;
		}

		void PostThis()
		{
			if(Wo->Wnd)
				SendMessage(Wo->Wnd, AE_LOOKUP_COMPLETED, (WPARAM)this, NULL);
		}

		virtual void Finish(USHORT RowIndex) = 0;

		virtual ~AclEditLookupCompletionBase()
		{}
	};

	template <class T> struct AclEditLookupCompletionBase2 : AclEditLookupCompletionBase, Lookup::CompletionTarget<T>
	{
		AclEditLookupCompletionBase2(ListboxAceRow *Row, UCHAR Col, AclEditorLookup *Wo, BOOLEAN NoErrorDisplay = FALSE) :
			AclEditLookupCompletionBase(Row, Col, Wo, NoErrorDisplay)
		{}
		virtual void CompleteExcept(EXCEPTION_RECORD const *ExceptionRecord) override
		{
			Se = ExceptionRecord;
			PostThis();
		}
	};
		
	struct AclEditSidLookupCompletion : AclEditLookupCompletionBase2<WCHAR>
	{
		auto_szpod<WCHAR> Name;

		AclEditSidLookupCompletion(ListboxAceRow *Row, AclEditorLookup *Wo) : 
			AclEditLookupCompletionBase2(Row, COL_TSTE_NAME, Wo)
		{}

		virtual void Complete(PCWSTR Result) override
		{
			Name = Result;
			PostThis();
		}

		virtual void Finish(USHORT RowIndex) override
		{
			SetListBoxItemText(Wo->Columns[Col].Wnd, RowIndex, Name);
		}
	};

	struct AclEditNameLookupCompletion : AclEditLookupCompletionBase2<SID>
	{
		auto_szpod<SID> Sid;

		AclEditNameLookupCompletion(ListboxAceRow *Row, AclEditorLookup *Wo) : 
			AclEditLookupCompletionBase2(Row, COL_TSTE_SID, Wo)
		{}
	
		virtual void Complete(SID const *Result) override
		{
			Sid = Result;
			PostThis();
		}

		virtual void Finish(USHORT RowIndex) override
		{				
			Wo->LookupSid(Row, Sid);

			auto_szpod<WCHAR> SidString = SidToText(Sid);

			SetListBoxItemText(Wo->Columns[Col].Wnd, RowIndex, SidString);
		}
	};

	struct AclEditObjectGuidLookupCompletion : AclEditLookupCompletionBase2<WCHAR>
	{
		auto_szpod<WCHAR> Name;

		AclEditObjectGuidLookupCompletion(ListboxAceRow *Row, UCHAR Col, AclEditorLookup *Wo) : 
			AclEditLookupCompletionBase2(Row, Col, Wo, TRUE)
		{}

		virtual void Complete(PCWSTR Result) override
		{
			Name = Result;
			PostThis();
		}

		virtual void Finish(USHORT RowIndex) override
		{				
			SetListBoxItemText(Wo->Columns[Col].Wnd, RowIndex, Name);
		}
	};

	struct AclEditObjectNameLookupCompletion : AclEditLookupCompletionBase2<GUID>
	{
		GUID Guid;

		AclEditObjectNameLookupCompletion(ListboxAceRow *Row, UCHAR Col, AclEditorLookup *Wo) : 
			AclEditLookupCompletionBase2(Row, Col, Wo)
		{}

		virtual void Complete(GUID const *Result) override
		{
			Guid = *Result;
			PostThis();
		}

		virtual void Finish(USHORT RowIndex) override
		{				
			if(Col == COL_OBJ_TYPE)
				Wo->LookupObjectTypeGuid(Row, Guid, Col);
			else
				Wo->LookupObjectInhTypeGuid(Row, Guid, Col);

			if(Col == COL_OBJ_TYPE)
				Row->TypeGuid = &Guid;
			else//Col == COL_OBJ_INH_TYPE
				Row->InhTypeGuid = &Guid;

			auto_szpod<WCHAR> GuidText = GuidToText(&Guid);

			SetListBoxItemText(Wo->Columns[Col].Wnd, RowIndex, GuidText);
		}
	};

	void LookupCompletedGuarded(AclEditLookupCompletionBase *lc, USHORT &RowIndex)
	{
		if(lc->UpdateIndex >= *lc->SrcUpdateIndex)
		{
			RowIndex = GetDataRowIndex(lc->Row);

			lc->Se.ReRaiseIf();

			lc->Finish(RowIndex);

			Scroll();
		}
	}

	void LookupCompletedSEH(AclEditLookupCompletionBase *lc, USHORT &RowIndex)
	{
		__try
		{
			LookupCompletedGuarded(lc, RowIndex);
		}
		__except((lc->NoErrorDisplay && IsExpectedException(exception_code())) ? EXCEPTION_EXECUTE_HANDLER : HandleLookupException(exception_info(), lc->Col, RowIndex))
		{}
	}

protected:
	LRESULT LookupCompleted(LPARAM lparam, WPARAM wparam)
	{
		AclEditLookupCompletionBase *lc = reinterpret_cast<AclEditLookupCompletionBase *>(wparam);
		USHORT RowIndex = MAXUSHORT;

		LookupCompletedSEH(lc, RowIndex);

		return 0;
	}

	void LookupSid(ListboxAceRow *Row, SID const *Sid)
	{
		SidTranslator->LookupData(new AclEditSidLookupCompletion(Row, this), Sid);
	}

	void LookupName(ListboxAceRow *Row, PCWSTR Name)
	{
		SidTranslator->LookupName(new AclEditNameLookupCompletion(Row, this), Name);
	}

	void LookupObjectTypeGuid(ListboxAceRow *Row, GUID const &Guid, UCHAR Col)
	{
		GuidTypeTranslator->LookupData(new AclEditObjectGuidLookupCompletion(Row, Col, this), &Guid);
	}

	void LookupObjectInhTypeGuid(ListboxAceRow *Row, GUID const &Guid, UCHAR Col)
	{
		GuidInhTypeTranslator->LookupData(new AclEditObjectGuidLookupCompletion(Row, Col, this), &Guid);
	}

	void LookupObjectTypeName(ListboxAceRow *Row, PCWSTR Name, UCHAR Col)
	{
		GuidTypeTranslator->LookupName(new AclEditObjectNameLookupCompletion(Row, Col, this), Name);
	}

	void LookupObjectInhTypeName(ListboxAceRow *Row, PCWSTR Name, UCHAR Col)
	{
		GuidInhTypeTranslator->LookupName(new AclEditObjectNameLookupCompletion(Row, Col, this), Name);
	}

	int HandleLookupException(EXCEPTION_POINTERS *ExceptionPointers, UCHAR Col, USHORT RowIndex)
	{
		if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_ROW_DELETED)
			return EXCEPTION_EXECUTE_HANDLER;

		if(RowIndex == MAXULONG)
			return EXCEPTION_EXECUTE_HANDLER;

		LOOKUP_EXCEPTION_CONTEXT LEContext;

		LEContext.ListBox = Columns[Col].Wnd;
		LEContext.RowIndex = RowIndex;

		if(PushLookupErrorText(ExceptionPointers, &LookupExceptionProc, Col == COL_TSTE_SID, &LEContext))
			return EXCEPTION_EXECUTE_HANDLER;
		else
			return EXCEPTION_CONTINUE_SEARCH; //unknown error
	}

	virtual LRESULT RecieveMessage(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) override
	{
		if(msg == AE_LOOKUP_COMPLETED)
			return LookupCompleted(lparam, wparam);

		return __super::RecieveMessage(wnd, msg, wparam, lparam);
	}
};